#!/bin/bash
set -o errexit

function usage(){
    echo "usage:"
    echo "  $0 <SUBCOMMAND> <options> "
    echo "      SUBCOMMAND:"
    echo "          check 检查所有活动package的使用状态(是否有虚拟机使用)列表"
    echo "              --user-uuid"
    echo "              --include-admin"
    echo "          list-vms 查询使用某个package的虚拟机列表, 必须提供--pkg-uuid 或 --pkg-name"
    echo "              --pkg-name"
    echo "              --pkg-uuid"
    echo "          list-noactive-use 查询所有被使用的非活动package列表"
    echo "          update-vm 更新vm的package"
    echo "              --vm-uuid"
    echo "              --vm-name"
    echo "              --pkg-uuid"
    echo "              --pkg-name"
    echo "              --pkg-version"
    echo "          create-pkg 创建 package json 文件"
    echo "              --cpu"
    echo "              --memory"
    echo "              --quota"
    echo "              --io"
    echo "              --brand"
    echo "          update-pkg 创建 package json 文件"
    echo "              -d"
    echo "          delete-pkg 创建 package json 文件"
    echo "              --pkg-uuid"
    echo "              --pkg-name"
    echo "              --pkg-version"
    echo "      options:" 
    echo "          -vu|--vm-uuid [VMUUID] 虚拟机 uuid, 如果提供此项，则 --vm-name 失效"
    echo "          -vn|--vm-name [VMNAME] 虚拟机 alias"    
    echo "          -pn|--pkg-name [PACKAGENAME] package名称"
    echo "          -pv|--pkg-version [PACKAGEVERSION] package版本"
    echo "          -pu|--pkg-uuid [PACKAGEUUID] package uuid, 当选择此项时，--pkg-name选项失效"
    echo "          -uu|--user-uuid [USERUUID] 所属用户uuid，可选，如果选择，则--include-admin失效"
    echo "          -d [JSON] json格式参数"
    echo "          -f|--save-file [SAVEFILE] 将结果保存到文件中，可选，默认不保存"
    echo "          --include-admin 是否包含 admin 专用package,可选，SUBCOMMAND 为check时有效，默认不包含"
    echo "          -h|--help 显示使用帮助"
    echo "      本脚本只能在triton headnode上运行"
    exit
}

function fatal(){
    echo "$1"
    exit 1
}

function wait_job(){
    if [ "$1" == "" ]; then
            echo ""
            return
    fi
    local j_id=$1
    local b_loop="true"
    local job_state="running"
    while [ "${b_loop}" == "true" ]
    do
        job_state=$(sdc-vmapi /jobs/${j_id}|json -H execution)
        if [ "${job_state}" == "succeeded" -o "${job_state}" == "failed" -o "${job_state}" == "canceled" ]; then
            b_loop="false"
        fi
        sleep 1s
    done
    echo ${job_state}
}

# set_package_active uuid active
#   uuid: package uuid
#   active: enable or disable
function set_package_active(){
    if [ "$1" == "" ]; then
            echo "没有提供参数uuid"
            return
    fi
    if [ "$2" != "true" -a "$2" != "false" ]; then
    echo "$2"
            echo "没有提供禁用启用标记"
            return
    fi

    local p_uuid=$1
    echo "sdc-papi /packages/${p_uuid} -X PUT -d '{\"active\":$2}'"
    sdc-papi /packages/${p_uuid} -X PUT -d "{\"active\":$2}"|json -H message
}

# get_package_uuid name
#   name: package name
#   return: uuid, if there's no package,then ""
function get_package_uuid(){
    if [ "$1" == "" ]; then
            echo ""
            return
    fi
    local name_verion=$1
    local nv=(${name_verion//@/ })
    local p_uuid=$(sdc-papi /packages?name=${nv[0]} | json -H -c "name=='${nv[0]}'" -c "version='${nv[1]}'" 0.uuid)
    echo "${p_uuid}"
}

# get_package_name uuid
#   uuid: package uuid
#   return: package name, if there's no package, then ""
function get_package_name(){
    if [ "$1" == "" ]; then
            echo ""
            return
    fi
    local full_name=$(sdc-papi /packages/$1 | json -H -e "this.fullname=this.name+'@'+this.version" fullname)
    echo ${full_name}
}

# 在数组中查找是否存在某元素值
# $1 要查询的元素值
# $2 数组，注意：传参形式 "$arrname[*]"
# result: 0 不存在该元素值，其他，该元素值在数组中的位置(从1开始)
function find_in_array(){
    local ele=$1
    local arr=$2
    local j=0
    for i in ${arr[@]}
    do
        j=$((j + 1))
        if [ "$i" == "${ele}" ];then
            echo "${j}"
            return
        fi
    done
    echo "0"    
}

# 检查package使用情况
#   如果没有 include_admin, 则不会包含 owner_uuids 显式包含 admin_uuid 的 package
#   如果设置了 user-uuid, 则只会包含没有设定 owner_uuids 和 显示包含 user-uuid 的package
#   result:
#       没有被使用packages 指没有被任何vm使用
#       已使用packages 指至少被一个状态不为destroyed的vm使用
function check()
{        
    local conditions="1==1"
    if [[ "${owner_uuid}" == "" ]];then
        [[ ${include_admin} == true ]] || conditions="this.owner_uuids==null || this.owner_uuids.indexOf('${admin_uuid}') == -1"
    else
        conditions="this.owner_uuids==null || this.owner_uuids.indexOf('${owner_uuid}') > -1"
    fi
    echo -n "load all active packages..."
    pkgs=($(sdc-papi /packages?active=true|json -H -c "${conditions}" -e "fullname=this.uuid+':'+this.name+':'+this.version" -a fullname))
    echo " DONE"

    pkg_inuse=()
    echo "没有被使用packages: "
    echo "package_uuid:package_name:package_version"
    echo "-----------------------------------------"
    for pkg in ${pkgs[@]}
    do
        pkguuid=${pkg%%:*}
        vms=$(get_pkg_vm_count ${pkguuid})
        if [[ ${vms} -eq 0 ]]; then
            echo "${pkg}"
        else
            #echo "${pkg}:${vms##* }"
            pkg_inuse[${#pkg_inuse[@]}]="${pkg}:${vms}"
        fi
    done
    echo ""
    echo "已使用packages: "
    echo "package_uuid:package_name:package_version:inuse_vm_count"
    echo "--------------------------------------------------------"
    for pkg in ${pkg_inuse[@]}
    do
        echo "${pkg}"
    done
}

# 查找使用某个package的vms
#   可以查看任意package(包括non-active)的vm使用情况
function list_vms(){
    local q_args=""
    [[ "${package_name}" == "" ]] || q_args="name=${package_name}"
    [[ "${package_uuid}" == "" ]] || q_args="uuid=${package_uuid}"
    [[ "${q_args}" == "" ]] && fatal "list-vms 必须提供--pkg-uuid 或 --pkg-name, 详情请运行$0 -h"
    local pkgs=($(sdc-papi /packages?${q_args} | json -H -e "fullname=this.uuid+':'+this.name+':'+this.version" -a fullname))
    echo "---package_uuid:package_name:package_version---"
    for pkg in ${pkgs[@]}
    do
        pkguuid=${pkg%%:*}
        vms=($(sdc-vmapi /vms?billing_id=${pkguuid}|json -H -c "state!='destroyed'" -e "fullname=this.uuid+':'+this.alias" -a fullname))
        if [ ${#vms[@]} -eq 0 ]; then
            echo "${pkg} is not used"
        else
            echo "${pkg} is used in:"
            echo "---vm_uuid:vm_alias---"
            for vm in ${vms[@]}
            do
                echo "${vm}"
            done
        fi
        echo ""
    done
}

# 查找被使用的非活动package列表
#   只查看被至少一个状态非destroyed的vm使用的非活动(active=false)的package
function list_noactive_use(){
    echo -n "load all non-active packages..."
    local pkgs=($(sdc-papi /packages?active=true|json -H -a uuid))
    echo " DONE"
    echo -n "load all non-destroyed vms..."
    local vms=($(sdc-vmapi /vms | json -H -c "state!='destroyed'" -e "fullname=this.uuid+':'+this.alias+':'+this.billing_id" -a fullname))
    echo " DONE"
    echo ""
    echo "非活动的package使用情况:"
    echo "package-uuid:package-name:package-version:package-active"
    echo "--------------------------------------------------------"
    for vm in ${vms[@]}
    do
        vmuuid=${vms%%:*}
        pkguuid=${vms##*:}
        idx=$(find_in_array ${pkguuid} "${pkgs[*]}")
        # pkguuid在活动package中没有找到
        if [ "${idx}" == "0" ];then
                pkg=$(sdc-papi /packages/${pkguuid} | json -H -e "fullname=this.uuid+':'+this.name+':'+this.version+':'this.active" -a fullname)
                echo "${pkg}"
        fi
    done
}

# 获取使用指定package的vm数量
function get_pkg_vm_count(){
    local pkguuid=$1

    local cnt=$(sdc-vmapi /vms?billing_id=${pkguuid}|json -H -c "state!='destroyed'" -a alias | wc -l)
    echo "${cnt##* }"    
}

function ensure_vm_exists(){
    if [ "${vm_uuid}" == "" ]; then
        if [ "${vm_name}" == "" ]; then
            usage
        fi
        vm_uuid=$(sdc-vmapi /vms?alias=${vm_name} | json -H -c "state!='destroyed' && alias=='${vm_name}'" 0.uuid)
        [[ "${vm_uuid}" != "" ]] || fatal "不存在vm: ${vm_name}"
    else
        vm_name=$(sdc-vmapi /vms/${vm_uuid} | json -H -c "state!='destroyed'" alias)
        [[ "${vm_name}" != "" ]] || fatal "不存在uuid为 ${vm_uuid} 的虚拟机"
    fi
}

function ensure_pkg_exists(){
    if [ "${package_uuid}" == "" ]; then
        if [ "${package_name}" == "" -a "${package_version}" == "" ]; then
               usage
        fi
        [[ "${package_name}" == "" ]] && package_name=${src_package_name}
        [[ "${package_version}" == "" ]] && package_version=${src_package_version}
        rx='^([1-9]?[0-9])(\.[1-9]?[0-9])(\.[1-9]?[0-9])$'
        [[ ${package_version} =~ ${rx} ]] || fatal "package version格式不对，正确格式为\d{1,2}.\d{1,2}.\d{1,2}"

        package_uuid=$(sdc-papi /packages?name=${package_name}|json -H -c "version=='${package_version}'" 0.uuid)
        [[ "${package_uuid}" != "" ]] || fatal "不存在name=${package_name} version=${package_version} 的 package"
    else
        package_name=$(sdc-papi /packages/${package_uuid} | json -H name)
        [[ "${package_name}" == "" ]] && fatal "不存在uuid=${package_uuid}的package"
        package_version=$(sdc-papi /packages/${package_uuid} | json -H version)
    fi
}

function update_vm(){

    ensure_vm_exists    

    local billing_id=$(sdc-vmapi /vms/${vm_uuid}|json -H billing_id)
    src_package_name=$(sdc-papi /packages/${billing_id}|json -H name)
    src_package_version=$(sdc-papi /packages/${billing_id}|json -H version)

    ensure_pkg_exists    

    [[ "${billing_id}" == "${package_uuid}" ]] && fatal "${vm_name}: package相同，不需要更新"

    echo "update package from ${src_package_name}@${src_package_version} to ${package_name}@${package_version}..."
    local error_code=$(sdc-vmapi /vms/${vm_uuid}?action=update -X POST -d "{\"billing_id\":\"${package_uuid}\"}"|json -H code)
    if [ "${error_code}" == "" ]; then
        echo "update success"
        exit
    elif [ "${error_code}" != "VmNotStopped" ]; then
        fatal "更新失败: error_code: ${error_code}"
    fi

    echo "${vm_name}: 关机..."
    local job_uuid=$(sdc-vmapi /vms/${vm_uuid}?action=stop -X POST | json -H job_uuid)
    [[ "${job_uuid}" == "" ]] && fatal "关机失败,命令: sdc-vmapi /vms/${vm_uuid}?action=stop -X POST"
    local job_state=$(wait_job ${job_uuid})

    if [ "${job_state}" == "succeeded" ]; then
        echo "${vm_name}: 更新package为 ${package_name}@${package_version}"
        job_uuid=$(sdc-vmapi /vms/${vm_uuid}?action=update -X POST -d "{\"billing_id\":\"${package_uuid}\"}" | tee -a ${log_file} | json -H job_uuid)
        [[ "${job_uuid}" == "" ]] || echo "update job state: $(wait_job ${job_uuid})"        
        echo "${vm_name}: 开机... " 
        sdc-vmapi /vms/${vm_uuid}?action=start -X POST | json -H
    else
        echo "${vm_name}: 关机失败 job[${job_uuid}] state:${job_state}"
    fi
}

# 更新package
function update_pkg(){

    [[ "${json_args}" != "" ]] || usage    

    ensure_pkg_exists
    sdc-papi /packages/${package_uuid} -X PUT -d "${json_args}"
}

# 删除package
# 只有在要删除的package没有被vm使用时才会删除
function delete_pkg(){
    ensure_pkg_exists
    local vm_cnt=$(get_pkg_vm_count ${package_uuid})
    if [ ${vm_cnt} -eq 0 ];then
        echo "delete package ${package_name}@${package_version}:${package_uuid}"
        sdc-papi /packages/${package_uuid}?force=true -X DELETE
    else
        echo "can not delete package ${package_name}@${package_version}:${package_uuid}, it is used by ${vm_cnt} vms"
    fi
}

# 支持的 SUBCOMMAND
support_commands=(check list-vms list-noactive-use update-vm update-pkg delete-pkg)

# 第一个参数为 SUBCOMMAND
subcommand=$1
[[ "${subcommand}" == "" || "${subcommand}" == "-h" || "${subcommand}" == "=-help" ]] && usage
idx=$(find_in_array ${subcommand} "${support_commands[*]}")
[[ "${idx}" == "0" ]] && fatal "不支持的Action: ${subcommand}，只支持以下Action: ${support_commands[*]}"

shift

include_admin=false
while [[ $# -ge 1 ]]; do
    case $1 in        
        -vu|--vm-uuid )
            vm_uuid=$2
            shift 2
            ;;
        -vn|--vm-name )
            vm_name=$2
            shift 2
            ;;
        -pn|--pkg-name )
            package_name=$2
            shift 2
            ;;
        -pv|--pkg-version )
            package_version=$2
            shift 2
            ;;
        -pu|--pkg-uuid )
            package_uuid=$2
            shift 2
            ;;
        -uu|--user-uuid )
            owner_uuid=$2
            shift 2
            ;;
        -d )
            json_args=$2
            shift 2
            ;;
        --include-admin )
            include_admin=true
            shift
            ;;
        -h|--help )
            usage
            ;;        
        * )
            usage
            ;;
    esac
done

# 管理员账号 uuid
admin_uuid=$(sdc-useradm search -Ho uuid admin)

case ${subcommand} in
    check )
        check
        ;;
    list-vms )
        list_vms
        ;;
    list-noactive-use )
        list_noactive_use
        ;;
    update-vm )
        update_vm
        ;;
    update-pkg )
        update_pkg
        ;; 
    delete-pkg )
        delete_pkg
        ;;        
    * )
        fatal "不支持的子命令 ${subcommand}"
esac